\chapter{GNU Regex的匹配寄存器}\label{gnu-app-registers}

GNU匹配寄存器是用于记录正则匹配中，正则表达式中各分组的匹配起始和结束位置的
数据结构。正则表达式的分组是指正则表达式中用小括号括起的部分，需要注意的是
整个正则表达式算作一个分组，记为默认分组(group 0)。

\section{结构体定义}
在\texttt{regex.h}中，匹配寄存器结构体的定义为：

\begin{minipage}{0.90\textwidth}
  \begin{ccode}
    struct re_registers
    {
        __re_size_t num_regs;
        regoff_t *start;
        regoff_t *end;
    };
  \end{ccode}
\end{minipage}

其成员\cinline{start}和\cinline{end}会在调用时进行分配，共计\cinline{num_regs + 1}%
个元素，第\cinline{i}个元素记录了正则表达式中中第\cinline{i}个分组的匹配信息。
\cinline{start[0]}和\cinline{end[0]}用于记录整个正则表达式的匹配信息(group 0)。

\section{匹配寄存器使用实例}

对于正则表达式中第\cinline{i}组匹配，搜索或匹配函数会将%
\cinline{regs->start[i]}设置为字符串中第\cinline{i}组匹配的子串开始的索引，并将%
\cinline{regs->end[i]}设置为该子串结束后紧跟的字符的索引。搜索或匹配函数将%
\cinline{regs->start[0]}和\cinline{regs->end[0]}设置为整个正则表达式的匹配信息
（字符串中的第一个字符位于索引0处）。

以下面的示例说明了在\cinline{struct re_registers}结构中记录的匹配信息。

例如，将\cinline{"((a)(b))"}与\cinline{"ab"}匹配，则得到：
\begin{itemize}
  \item \cinline{regs->start[0]}的值为0，\cinline{regs->end[0]}的值为2
  \item \cinline{regs->start[1]}的值为0，\cinline{regs->end[1]}的值为2
  \item \cinline{regs->start[2]}的值为0，\cinline{regs->end[2]}的值为1
  \item \cinline{regs->start[3]}的值为1，\cinline{regs->end[3]}的值为2
\end{itemize}

如果一个组匹配了多次（例如，如果后面跟了一个重复运算符），那么会保存该组最后
一次匹配的信息。

例如，将\cinline{"(a)*"}与\cinline{"aa"}匹配，则得到：
\begin{itemize}
  \item \cinline{regs->start[0]}的值为0，\cinline{regs->end[0]}的值为2
  \item \cinline{regs->start[1]}的值为1，\cinline{regs->end[1]}的值为2
\end{itemize}

如果第\cinline{i}组不参与成功匹配，例如，它是一个未被选中的选项，或者重复
运算符允许其重复次数为零，那么会将\cinline{regs->start[i]}和%
\cinline{regs->end[i]}设置为\cinline{-1}。

例如，将\cinline{"(a)*b"}与\cinline{"b"}匹配，则得到：
\begin{itemize}
  \item \cinline{regs->start[0]}的值为0，\cinline{regs->end[0]}的值为1
  \item \cinline{regs->start[1]}的值为-1，\cinline{regs->end[1]}的值为-1
\end{itemize}

如果第\cinline{i}组匹配的长度为零，那么会将\cinline{regs->start[i]}和%
\cinline{regs->end[i]}设置为紧跟的0。

例如，将\cinline{"(a*)b"}与\cinline{"b"}匹配，则得到：
\begin{itemize}
  \item \cinline{regs->start[0]}的值为0，\cinline{regs->end[0]}的值为1
  \item \cinline{regs->start[1]}的值为0，\cinline{regs->end[1]}的值为0
\end{itemize}

如果第\cinline{i}组依次包含第\cinline{j}组，而第\cinline{i}组中的任何其他组
都不包含第\cinline{j}组，并且设置了第\cinline{i}组的匹配结果，那么它就会在
\cinline{regs->start[j]}和\cinline{regs->end[j]}中记录第\cinline{j}组的最后
一次匹配结果（如果匹配成功）。

例如，将\cinline{"((a*)b)*"}与\cinline{"abb"}匹配，第 2 组最后匹配的是空字符串，
因此会得到它之前匹配的结果：
\begin{itemize}
  \item \cinline{regs->start[0]}的值为0，\cinline{regs->end[0]}的值为3
  \item \cinline{regs->start[1]}的值为2，\cinline{regs->end[1]}的值为3
  \item \cinline{regs->start[2]}的值为2，\cinline{regs->end[2]}的值为2
\end{itemize}

例如，将\cinline{"((a)*b)*"}与\cinline{"abb"}匹配，第 2 组不参与最后一次匹配，
则得到：
\begin{itemize}
  \item \cinline{regs->start[0]}的值为0，\cinline{regs->end[0]}的值为3
  \item \cinline{regs->start[1]}的值为2，\cinline{regs->end[1]}的值为3
  \item \cinline{regs->start[2]}的值为0，\cinline{regs->end[2]}的值为1
\end{itemize}

如果第\cinline{i}组依次包含第\cinline{j}组，而第\cinline{i}组中的任何其他组
都不包含第\cinline{j}组，并且设置了第\cinline{i}组的匹配结果，那么它就会在
\cinline{regs->start[j]}和\cinline{regs->end[j]}中记录第\cinline{j}组的最后
一次匹配结果（如果匹配成功）。

如果第\cinline{i}组依次包含不包含在第\cinline{i}组内任何其他组中的第\cinline{j}组，
并且将\cinline{regs->start[i]} 和\cinline{regs->end[i]}设置为-1，那么它也会将%
\cinline{regs->start[j]}和\cinline{regs->end[j]} 设置为-1。

例如，将\cinline{"((a)*b)*c"}与\cinline{"c"}匹配，则得到：
\begin{itemize}
  \item \cinline{regs->start[0]}的值为0，\cinline{regs->end[0]}的值为1
  \item \cinline{regs->start[1]}的值为-1，\cinline{regs->end[1]}的值为-1
  \item \cinline{regs->start[2]}的值为-1，\cinline{regs->end[2]}的值为-1
\end{itemize}
